-- ===========================
-- Analyze the data in the test runs folders
-- ===========================
-- Map Sizes
dofile("MapSizes.lua")

dofile("MEC.lua")

dofile("formats.lua")

local debug = true

-- ===========================
-- Convert from (x,y) map coordinates to in-game yard coordinates.
-- ===========================
local function CoordsToPosition(map, x, y)
	if not map then
		return 0, 0
	end
	return x * zoneScalingData[map][1], y * zoneScalingData[map][2]
end

-- ===========================
-- Export the player position data into text files according to maps.
-- Also handles the MEC calculation
-- ===========================
function CreatePositionMECDataFile(directory, sessionFilename, session)
	-- Reverse the maps
	local mapTimeline = session.globalTimelines.mapTimeline
	local maps = {}
	
	-- Explore both the dictionary and the index portions of the table
	-- as seen at the bottom of http://www.lua.org/pil/4.3.5.html
	for k, map in pairs(mapTimeline) do maps[map] = {} end
	for i, map in ipairs(mapTimeline) do maps[map] = {} end
	
	----------------------------------------------------
	-- Create file handles for each map visited, and set up the comments for each column. 
	local files = {}
	for map in pairs(maps) do
		local filename = sessionFilename .. tostring(map) .. extensions.data
		local path = directory .. "/"  .. filename
		files[map] = io.open(path, "w")
		-- Set the current output to the file.
		io.output(files[map])
		
		-- Write the map name to identify the area.
		io.write("#Map: " .. tostring(map) .. "\n")
		
		-- Identify the sequence number column.
		io.write("#SequenceNumber center_x center_y radius_original radius_scaled ")
		
		-- Identify the player columns.
		for k,v in pairs (session.players) do
			local name = session.players[k].name
			io.write(name .. "_x " .. name .. "_y ")
		end
		io.write("\n")
	end

	----------------------------------------------------
	-- Create the file handle for the radius plot
	local mecFilename = sessionFilename .. filenameSuffixes.mec .. extensions.data
	local path = directory .. "/" .. mecFilename
	local mecFile = io.open(path, "w")
	io.output(mecFile)
	io.write("#Minimum Enclosing Circle radius progression\n")
	io.write("#SequenceNumber radius_scaled\n")

	----------------------------------------------------
	-- Create a new Minimum Enclosing Circle solver.	
	local mec = MEC:New()
	local scale = {1,1,1}
	
	----------------------------------------------------	
	-- Sequentially write the position of each player, making sure to switch
	-- maps when necessary.
	local previousMap = nil
	-- Write the unit data for each sequence number.
	local numLines = session.sequenceNumber - 1
	for i = 1, numLines do
		----------------------------------------------------
		-- Check if we have to change our file output.
		local currentMap = mapTimeline[i] -- If currentMap is nil, then we don't have to do anything.
		if currentMap and currentMap ~= previousMap then
			-- Switch the output to the appropriate file.
			io.output(files[currentMap])
			-- Set the map scaling values to the new map.
			scale = zoneScalingData[currentMap]
			-- Insert the current sequence number "i" into the current map table,
			-- and the previous sequence number into the previous map table,
			-- to denote a break.
			if previousMap then	table.insert(maps[previousMap], i-1) end
			table.insert(maps[currentMap], i)
			-- Set the previous map to the current map.
			previousMap = currentMap
		end
		
		-- Make sure we record the very last sequence number in the current map.
		if i == numLines then
			table.insert(maps[previousMap], i)
		end
		
		----------------------------------------------------
		-- Column[1]: Write the sequence number at the beginning of the line.
		io.write(tostring(i) .. " ")
		
		----------------------------------------------------
		-- Columns[2,3,4,5]: Compute and write the MEC data.
		local points = {}
		-- Grab all the player points.
		for k, v in pairs(session.players) do
			local timelines = v.timelines
			local px = timelines.xPositionTimeline[i]
			local py = timelines.yPositionTimeline[i]
			if px > 0 and py > 0 then
				table.insert(points, {x = px, y = py})
			end 
		end
		
		-- Set the MEC solver's points.
		mec:SetPoints(points)
		--[[
		for i,p in ipairs(points) do
			print("points[" .. tostring(i) .. "]:" .. tostring(p))
		end
		]]--
		
		-- Compute the MEC
		local circle = mec:Compute()
		-- Scale the radius.
		local dx = (circle.center.x - circle.radiusPoint.x)
		local dy = (circle.center.y - circle.radiusPoint.y)
		local r = circle.radius --math.sqrt(dx*dx + dy*dy)
		dx = dx * scale[1]
		dy = dy * scale[2]
		local scaledRadius = math.sqrt(dx*dx + dy*dy)
		--local scaledRadius = circle.radius * scale[1]
		if scaledRadius > 60 then
			io.write("?")
		end
		
		-- Write the MEC data.
		io.write(tostring(circle.center.x) .. " " .. 
				tostring(circle.center.y) .. " " .. 
				tostring(r) .. " " ..
				--tostring(circle.radius) .. " " .. 
				tostring(scaledRadius) .. " ")
				
		----------------------------------------------------
		-- Record the scaled radius in its own file, then revert
		-- back to the map file we are writing to.
		local push = files[previousMap]
		io.output(mecFile)
		io.write(tostring(i) .. " " .. tostring(scaledRadius) .. "\n")
		io.output(push)
		
		----------------------------------------------------
		-- Columns[6...]: Proceed to write the data.
		-- For each player, write his x and y position.
		for k, v in pairs(session.players) do
			-- Retrieve the x and y positions.
			local timelines = session.players[k].timelines
			local x = timelines.xPositionTimeline[i]
			local y = timelines.yPositionTimeline[i]
			
			-- Prefix the point with a "?" to tell gnuplot to ignore it when plotting,
			-- since (0,0) is usually an invalid position.
			if x == 0 then x = "?" .. tostring(x) end
			if y == 0 then y = "?" .. tostring(y) end
			
			-- Write it to the file.
			io.write(tostring(x) .. " " .. tostring(y) .. " ")
		end
		io.write("\n")
	end
	
	----------------------------------------------------
	-- Close all the open files.
	for mapname, file in pairs(files) do
		file:close()
	end
	mecFile:close()
	
	----------------------------------------------------
	-- Return the table of all the maps appearing in the session.
	return maps
end

-- ===========================
-- Create the Gnuplot script to plot the player position data according to each map visited.
-- ===========================
function CreatePositionMECGnuplotScript(directory, sessionFilename, outputFilename, format, session, maps)
	-- Open the file.
	local filename = outputFilename .. extensions.gnuplot
	local path = directory .. "/"  .. filename
	local gnuplotScriptFile = io.open(path, "w")
	io.output(gnuplotScriptFile)
	
	-- Write the titles and labels
	io.write("#Orunj Recording session: " .. sessionFilename .. "\n")
	io.write("reset\n")
	-- Make one plot per map.
	for map, breakTable in pairs(maps) do
		-- Get the data filename.
		local dataFilename = sessionFilename .. tostring(map) .. extensions.data
		
		-- Format before we plot.
		if format then
			-- redirect output to the specified place
			io.write("set terminal " .. format.name .. "\n")
			io.write("set output \"" .. sessionFilename .. tostring(map) .. format.extension .. "\"\n")
		else
			-- defaults
			io.write("set terminal wxt\n") -- wxt is a nice window terminal.
			io.write("set output\n")
		end
		
		-- Set the scale and labels.
		io.write("set title \"Player position on " .. tostring(map) .. "\"\n")
		io.write("set autoscale\n")
		io.write("set xrange [0:1]\n")
		io.write("set yrange [0:1]\n")
		--io.write("set xlabel \"x position\"\n")
		--io.write("set ylabel \"y position\"\n")
		
		-- Get the local player.
		local localPlayer = nil
		for guid, player in pairs(session.players) do
			if player.me == true then
				localPlayer = player
				break
			end
		end
		
		-- Label all the transitions and their sequence number.
		io.write("unset label\n")
		if localPlayer then
			for i, sequenceNumber in ipairs(breakTable) do 
				local x = localPlayer.timelines.xPositionTimeline[sequenceNumber]
				local y = localPlayer.timelines.yPositionTimeline[sequenceNumber]
				io.write("set label \"Sequence#: " .. tostring(sequenceNumber) .. "\" at " .. tostring(x) .. "," .. tostring(y) .. "\n")
			end
		end
		
		local currentPlayerColumn = 6
		local xIndex = 1
		local yIndex = 2
		local columnOffsets = {[xIndex] = 0, [yIndex] = 1}
		local first = true
		io.write("plot ")
		-- Draw the circles on this map.
		io.write("\"" .. dataFilename .. "\" ")
		--io.write("set style fill transparent solid 0.2 noborder\n")
		io.write("using 2:3:4 notitle with circles lc rgb 'orange' fs transparent solid 0.02 noborder, ")
		
		-- Draw the player lines		
		for k,v in pairs(session.players) do
			-- Make sure we have commas seperating the entries after the first entry.
			if not first then
				io.write(" , ")
			end
			
			-- Health column for the player.
			io.write("\"" .. dataFilename .. "\" ")
			io.write("using " .. tostring(currentPlayerColumn + columnOffsets[xIndex]))
			io.write(":" .. tostring(currentPlayerColumn + columnOffsets[yIndex]) .. " ")
			io.write("title '" .. v.name .. " (" .. v.class .. " - " .. tostring(v.role) .. ") Position' ")
			--io.write("with linespoints ")
			io.write("with lines ")
			
			currentPlayerColumn = currentPlayerColumn + #columnOffsets
			first = false		
		end
		
		io.write("\n")
		io.write("set output\n")
	end
	----------------------------------------------------
	-- Write the MEC radius gnuplot script
	-- Format before we plot.
	if format then
		-- redirect output to the specified place
		io.write("set terminal " .. format.name .. "\n")
		io.write("set output \"" .. sessionFilename .. filenameSuffixes.mec .. format.extension .. "\"\n")
	else
		-- defaults
		io.write("set terminal wxt\n") -- wxt is a nice window terminal.
		io.write("set output\n")
	end	
	dataFilename = sessionFilename .. filenameSuffixes.mec .. extensions.data
	-- Set the title, scale and labels.
	io.write("set title \"Minimum Enclosing Circle\"\n")
	io.write("set autoscale\n")
	io.write("set xlabel \"Sequence Number\"\n")
	io.write("set ylabel \"Radius in Yards\"\n")
	io.write("unset label\n")
	io.write("plot ")
	io.write("\"" .. dataFilename .. "\" ")
	io.write("using 1:2 ") 
	io.write("title 'MEC Radius progression' ")
	io.write("with lines ")
	io.write("\n")
	io.write("set output\n")

	-- close the file handle
	gnuplotScriptFile:close()
	return filename
end

-- ===========================
-- Create the intensity data file to be plotted later.
-- ===========================
local decayFactor = -10
function CreateIntensityDataFile(directory, sessionFilename, session)
	-- Open the file.
	local filename = sessionFilename .. extensions.data
	local path = directory .. "/"  .. filename
	local dataFile = io.open(path, "w")
	io.output(dataFile)
	
	-- Tracks the previous intensity of each player.
	local playerIntensity = {}
	
	-- Write to the file in a space-seperated vector format.
	-- #UnitHealth1 UnitPower1 UnitHealth2 UnitPower2 ..
	-- Set up the identification for each player.
	io.write("#SequenceNumber ")
	io.write("CumulativeIntensity ")
	for k, v in pairs (session.players) do
		local name = session.players[k].name
		io.write(name .. "_Intensity" .. " ")
		playerIntensity[k] = {previous = nil, accumulation = 0}
	end
	io.write("\n")
	
	-- Write the unit data for each sequence number.
	local numLines = session.sequenceNumber - 1
	for i = 1, numLines do
		-- Create a temporary table used later to write the line out.
		local lineToWrite = {}
		lineToWrite.cumulativeIntensity = 0
		lineToWrite.sequence = i
		
		local groupHealth = 0
		-- For each player, write his UnitHealth and UnitPower.
		for k, v in pairs(session.players) do
			groupHealth = groupHealth + v.maxHealth
			local timelines = session.players[k].timelines
			local unitHealth = timelines.unitHealthTimeline[i]
			-- local unitPower = timelines.unitPowerTimeline[i]
			
			if not playerIntensity[k].previous then
				playerIntensity[k].intensity = 0
				playerIntensity[k].previous = unitHealth
			else
				local previousHealth = playerIntensity[k].previous
				local healthDifference =  unitHealth - previousHealth
				-- A positive or zero healthDifference means there has not been
				-- any health loss, so we decay the intensity by a factor.
				if healthDifference >= 0 then
					-- do nothing.
					--playerIntensity[k].intensity = playerIntensity[k].intensity + decayFactor
					playerIntensity[k].intensity = 0
				else
					-- A negative healthDifference means there has been a loss in health,
					-- so we increment the intensity by the health loss. 
					--playerIntensity[k].intensity = playerIntensity[k].intensity + math.abs(healthDifference)
					playerIntensity[k].intensity = math.abs(healthDifference)
				end
				-- Record the previous health.
				playerIntensity[k].previous = unitHealth
			end
			
			-- increment the cumulative player intensity to write it later.
			lineToWrite.cumulativeIntensity = lineToWrite.cumulativeIntensity + playerIntensity[k].intensity
			
			-- add to the current line.
			table.insert(lineToWrite, playerIntensity[k].intensity)
		end
		
		-- write the line to the file.
		io.write(tostring(lineToWrite.sequence) .. " " .. lineToWrite.cumulativeIntensity / groupHealth .. " ")
		for i, intensity in ipairs(lineToWrite) do
			io.write(tostring(intensity) .. " ")
		end
		
		io.write("\n")
	end 
	
	-- close the file.
	dataFile:close()
end

-- ===========================
-- Create the gnuplot script to plot the intensity.
-- ===========================
function CreateIntensityGnuplotScript(directory, sessionFilename, outputFilename, format, session)
	-- Open the file.
	local filename = outputFilename .. extensions.gnuplot
	local path = directory .. "/"  .. filename
	local gnuplotScriptFile = io.open(path, "w")
	io.output(gnuplotScriptFile)
	
	local me = nil
	for k,v in pairs(session.players) do
		if v.me then
			me = v.name
		end
	end
	me = me or ""
	
	-- Write the titles and labels
	io.write("#Orunj Recording session: " .. sessionFilename .. "\n")
	io.write("reset\n")
	if format then
		-- redirect output to the specified place
		io.write("set terminal " .. format.name .. "\n")
		io.write("set output \"" .. outputFilename .. format.extension .. "\"\n")
	else
		-- defaults
		io.write("set terminal wxt\n") -- wxt is a nice window terminal.
		io.write("set output\n")
	end
	io.write("set autoscale\n")
	io.write("set title \"Group Intensity - " .. me .."\"\n")
	io.write("set xlabel \"Sequence number\"\n")
	io.write("set ylabel \"Intensity\"\n")
	io.write("set yrange [0:0.4]\n")
	
	-- Intensity plot
	local sequenceColumn = 1
	local cumulativeColumn = 2
	local currentPlayerColumn = 3
	local first = true
	
	io.write("plot ")
	first = true
	currentPlayerColumn = 3
	-- Plot the cumulative intensity first.
	io.write("\"" .. sessionFilename .. extensions.data .. "\" ")
	io.write("using " .. tostring(sequenceColumn))
	io.write(":" .. tostring(cumulativeColumn) .. " ")
	io.write("title 'Cumulative intensity' ")
	io.write("with lines ")
	--[[
	io.write(", ")
	-- Plot each player's intensity after.
	for k,v in pairs(session.players) do
		-- Make sure we have commas seperating the entries after the first entry.
		if not first then
			io.write(" , ")
		end
		
		-- Intensity for the player.
		io.write("\"" .. sessionFilename .. extensions.data .. "\" ")
		io.write("using " .. tostring(sequenceColumn))
		io.write(":" .. tostring(currentPlayerColumn) .. " ")
		io.write("title '" .. v.name .. " (" .. v.class .. " - " .. tostring(v.role) .. ") Intensity' ")
		io.write("with lines ")
		
		currentPlayerColumn = currentPlayerColumn + 1
		first = false		
	end
	]]--
	io.write("\n")
	io.write("set output\n")
	
	-- close the file.
	gnuplotScriptFile:close()
	return filename
end
